package nbcp.base.config

import org.apache.commons.dbcp2.BasicDataSource
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Component
import org.mybatis.spring.SqlSessionTemplate
import org.apache.ibatis.session.SqlSessionFactory
import org.mybatis.spring.SqlSessionFactoryBean
import org.springframework.jdbc.datasource.DataSourceTransactionManager
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.TransactionManagementConfigurer
import org.springframework.transaction.annotation.EnableTransactionManagement
import javax.sql.DataSource
import org.mybatis.spring.mapper.MapperScannerConfigurer
import org.springframework.boot.autoconfigure.AutoConfigureAfter
import nbcp.db.mybatis.QueryInterceptor
import nbcp.db.mybatis.UpdateInterceptor
import javax.annotation.Resource
import org.apache.commons.compress.archivers.zip.ExtraFieldUtils.UnparseableExtraField.READ
import springfox.documentation.schema.property.BeanPropertyDefinitions.name
import com.sun.xml.internal.ws.encoding.xml.XMLMessage.getDataSource
import nbcp.base.utils.MyUtil
import nbcp.base.utils.SpringUtil
import org.apache.ibatis.executor.CachingExecutor
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
import org.springframework.transaction.TransactionDefinition
import springfox.documentation.schema.property.BeanPropertyDefinitions.name
import java.util.Locale.CHINA
import org.apache.ibatis.mapping.BoundSql
import org.apache.ibatis.executor.keygen.SelectKeyGenerator
import org.apache.ibatis.mapping.SqlCommandType
import org.apache.ibatis.mapping.MappedStatement
import org.apache.ibatis.plugin.*
import org.springframework.transaction.support.TransactionSynchronizationManager
import java.util.concurrent.ConcurrentHashMap
import org.apache.ibatis.session.RowBounds
import org.apache.ibatis.session.ResultHandler
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Primary
import java.util.*
import org.apache.ibatis.executor.Executor
import org.mybatis.spring.transaction.SpringManagedTransaction


@Component
@ConfigurationProperties("spring.mysql.write")
class MySqlConfig_Write {

    var driverClassName = "com.mysql.cj.jdbc.Driver"
    var username = ""
    var password = ""
    var url = ""
    var maxTotal = 128;
    var maxIdel = 128;
    var maxWaitMillis = 30000L

    @Bean(name = arrayOf("write"))
    @Primary
    fun dataSource(): BasicDataSource {
//        println("BasicDataSource inited: ${url}")
        val dataSource = BasicDataSource()
        dataSource.driverClassName = driverClassName
        dataSource.url = url
        dataSource.username = username
        dataSource.password = password
        dataSource.maxTotal = maxTotal
        dataSource.maxIdle = maxIdel
        dataSource.maxWaitMillis = maxWaitMillis
        dataSource.setValidationQuery("SELECT 1")
        dataSource.testOnBorrow = true
        return dataSource
    }
}


@Component
@ConfigurationProperties("spring.mysql.read")
class MySqlConfig_Read {

    var driverClassName = "com.mysql.cj.jdbc.Driver"
    var username = ""
    var password = ""
    var url = ""
    var maxTotal = 128;
    var maxIdel = 128;
    var maxWaitMillis = 30000L

    @Bean(name = arrayOf("read"))
    fun dataSource(): BasicDataSource {
//        println("BasicDataSource inited: ${url}")
        val dataSource = BasicDataSource()
        dataSource.driverClassName = driverClassName
        dataSource.url = url
        dataSource.username = username
        dataSource.password = password
        dataSource.maxTotal = maxTotal
        dataSource.maxIdle = maxIdel
        dataSource.maxWaitMillis = maxWaitMillis
        dataSource.setValidationQuery("SELECT 1")
        dataSource.testOnBorrow = true
        return dataSource
    }
}

//
//@Configuration
//class config {
//
//}
//
//
//enum class DynamicDataSourceGlobal {
//    READ, WRITE
//}
//
//object DynamicDataSourceHolder {
//    private val holder = ThreadLocal<DynamicDataSourceGlobal>()
//
//    val dataSource: DynamicDataSourceGlobal
//        get() = holder.get()
//
//    fun putDataSource(dataSource: DynamicDataSourceGlobal) {
//        holder.set(dataSource)
//    }
//
//    fun clearDataSource() {
//        holder.remove()
//    }
//}//
//
//@Component
//class DynamicDataSource : AbstractRoutingDataSource() {
//
//    @Resource(name = "write")
//    lateinit var writeDataSource: BasicDataSource //写数据源
//
//    @Resource(name = "read")
//    lateinit var readDataSource: BasicDataSource  //读数据源
//
//    override fun afterPropertiesSet() {
//        val targetDataSources = HashMap<Any, Any>()
//        targetDataSources[DynamicDataSourceGlobal.WRITE] = writeDataSource
//        targetDataSources[DynamicDataSourceGlobal.READ] = readDataSource
//
//        setTargetDataSources(targetDataSources)
//
//        super.afterPropertiesSet()
//    }
//
//    override fun determineCurrentLookupKey(): Any {
//
//        val dynamicDataSourceGlobal = DynamicDataSourceHolder.dataSource
//
//        return if (dynamicDataSourceGlobal == null || dynamicDataSourceGlobal === DynamicDataSourceGlobal.WRITE) {
//            DynamicDataSourceGlobal.WRITE
//        } else DynamicDataSourceGlobal.READ
//
//    }
//}
//
//@Component
//class DynamicDataSourceTransactionManager : DataSourceTransactionManager() {
//    @Resource(name = "write")
//    lateinit var writeDataSource: BasicDataSource //写数据源
//
//    override fun afterPropertiesSet() {
//        this.dataSource = writeDataSource
//    }
//
//    /**
//     * 只读事务到读库，读写事务到写库
//     * @param transaction
//     * @param definition
//     */
//    override fun doBegin(transaction: Any, definition: TransactionDefinition) {
//
//        //设置数据源
//        val readOnly = definition.isReadOnly
//        if (readOnly) {
//            DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.READ)
//        } else {
//            DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.WRITE)
//        }
//        super.doBegin(transaction, definition)
//    }
//
//    /**
//     * 清理本地线程的数据源
//     * @param transaction
//     */
//    override fun doCleanupAfterCompletion(transaction: Any) {
//        super.doCleanupAfterCompletion(transaction)
//        DynamicDataSourceHolder.clearDataSource()
//    }
//}

//
//@Intercepts(
//        Signature(type = Executor::class, method = "update", args = arrayOf(MappedStatement::class, Any::class)),
//        Signature(type = Executor::class, method = "query", args = arrayOf(MappedStatement::class, Object::class, RowBounds::class, ResultHandler::class))
//)
//class DynamicPlugin : Interceptor {
//    @Throws(Throwable::class)
//    override fun intercept(invocation: Invocation): Any {
//        val synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive()
//        if (!synchronizationActive) {
//
//            val objects = invocation.getArgs()
//            val ms = objects[0] as MappedStatement
//
//            //读方法
//            if (ms.sqlCommandType == SqlCommandType.SELECT) {
//                //!selectKey 为自增id查询主键(SELECT LAST_INSERT_ID() )方法，使用主库
//                if (ms.id.contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
//                    setWriteMode(invocation.target as Executor);
//                } else {
//                    val boundSql = ms.sqlSource.getBoundSql(objects[1])
//                    val sql = boundSql.sql.toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]".toRegex(), " ")
//                    if (sql.matches(REGEX.toRegex())) {
//                        setWriteMode(invocation.target as Executor);
//                    } else {
//                        setReadMode(invocation.target as Executor);
//                    }
//                }
//            } else {
//                setWriteMode(invocation.target as Executor);
//            }
//        }
//        return invocation.proceed()
//    }
//
//
//    private fun setReadMode(executor: Executor) {
//        MyUtil.setPrivatePropertyValue(executor.transaction as SpringManagedTransaction, "dataSource", readDataSource)
//    }
//
//    //默认
//    private fun setWriteMode(executor: Executor) {
//        //MyUtil.setPrivatePropertyValue(executor.transaction as SpringManagedTransaction, "dataSource", writeDataSource)
//    }
//
//    override fun plugin(target: Any): Any {
//        return if (target is Executor) {
//            Plugin.wrap(target, this)
//        } else {
//            target
//        }
//    }
//
//    override fun setProperties(properties: Properties) {
//        //
//    }
//
//    companion object {
//        private val REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*"
//        private val readDataSource:BasicDataSource by lazy {
//            return@lazy SpringUtil.getBean<BasicDataSource>("read")
//        }
//    }
//}


@Component
@AutoConfigureAfter(MySqlConfig_Write::class)
class MyBatisSessionConfig {
    @Bean
    fun mapperScannerConfigurer(): MapperScannerConfigurer {
        val mapperScannerConfigurer = MapperScannerConfigurer()
        //获取之前注入的beanName为sqlSessionFactory的对象
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory")
        //指定xml配置文件的路径
        mapperScannerConfigurer.setBasePackage("nbcp.db.mybatis.mapper")
        return mapperScannerConfigurer
    }
}


@Configuration
//加上这个注解，使得支持事务
@EnableTransactionManagement
open class MyBatisConfig : TransactionManagementConfigurer {
    @Resource(name = "write")
    lateinit var dataSource: DataSource

    override fun annotationDrivenTransactionManager(): PlatformTransactionManager {
        return DataSourceTransactionManager(dataSource)
    }

    @Bean(name = arrayOf("sqlSessionFactory"))
    open fun sqlSessionFactoryBean(): SqlSessionFactory? {
        val bean = SqlSessionFactoryBean()
        bean.setDataSource(dataSource)

        var config = org.apache.ibatis.session.Configuration()
        config.isCacheEnabled = true

//        config.addCache(RedisCacheMyBatis())

        config.addInterceptor(QueryInterceptor())
        config.addInterceptor(UpdateInterceptor())
//        config.addInterceptor(DynamicPlugin())

        bean.setConfiguration(config)
        try {
            return bean.`object`
        } catch (e: Exception) {
            e.printStackTrace()
            throw RuntimeException(e)
        }

    }

    @Bean
    open fun sqlSessionTemplate(sqlSessionFactory: SqlSessionFactory): SqlSessionTemplate {
        return SqlSessionTemplate(sqlSessionFactory)
    }
}